package org.oos.dao.options;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.oos.util.Log;

import com.google.api.client.util.Data;

/**
 * Definition of the options that are used with the DAO objects to provide with
 * 
 * @author pablo.gonzalez@11870.com
 * 
 */
public abstract class OptionsMap implements Serializable {

	private static final long serialVersionUID = 5545426638494094527L;

	public static final class FINDERS {
		public static final OptionsFinder<FieldOptions> FIELD_OPTIONS = new OptionsFinder<FieldOptions>(
				FieldOptions.class);
		public static final OptionsFinder<FilterOptions> FILTER_OPTIONS = new OptionsFinder<FilterOptions>(
				FilterOptions.class);
		public static final OptionsFinder<LocationOptions> LOCATION_OPTIONS = new OptionsFinder<LocationOptions>(
				LocationOptions.class);
		public static final OptionsFinder<PageOptions> PAGE_OPTIONS = new OptionsFinder<PageOptions>(
				PageOptions.class);
		public static final OptionsFinder<QueryOptions> QUERY_OPTIONS = new OptionsFinder<QueryOptions>(
				QueryOptions.class);
		public static final OptionsFinder<CheckinOptions> CHECKIN_OPTIONS = new OptionsFinder<CheckinOptions>(
				CheckinOptions.class);
		public static final OptionsFinder<OrderOptions> ORDER_OPTIONS = new OptionsFinder<OrderOptions>(
				OrderOptions.class);
		public static final OptionsFinder<ScopeOptions> SCOPE_OPTIONS = new OptionsFinder<ScopeOptions>(
				ScopeOptions.class);
		public static final OptionsFinder<OfferOptions> OFFER_OPTIONS = new OptionsFinder<OfferOptions>(OfferOptions.class);
	}

	public static class OptionsFinder<O extends OptionsMap> {

		private transient Class<O> type;
		private final String className;

		public OptionsFinder(Class<O> type) {
			this.type = type;
			this.className = type.getName();
		}
		
		@SuppressWarnings("unchecked")
		public Class<O> getType() {
			if(type == null) {
				try {
					type = (Class<O>) Class.forName(className);
				} catch (ClassNotFoundException e) {
					Log.e(e);
				}
			} 
			return type;
		}

		@SuppressWarnings("unchecked")
		public List<O> findList(List<OptionsMap> list) {
			List<O> result = new ArrayList<O>();
			for (OptionsMap map : list) {
				if (map.getClass() == getType()) {
					result.add((O) map);
				}
			}
			return result;
		}

		public O find(List<OptionsMap> list) {
			List<O> result = findList(list);
			if (result.size() > 0) {
				return result.get(0);
			}
			return null;
		}

		/**
		 * It looks up of the provided Type of {@link OptionsMap} and if it
		 * doesn't exist it creates a new one.
		 * 
		 * @param <O>
		 * @param list
		 * @param feedType
		 * @return
		 */
		public O get(List<OptionsMap> list) {
			List<O> result = findList(list);
			if (result.size() > 0) {
				return result.get(0);
			}
			try {
				O map = getType().newInstance();
				list.add(map);
				return map;
			} catch (Exception e) {
				Log.e(e);
			}
			return null;
		}

		/**
		 * 
		 * Removes all the instances of the relevant OptionsMap.
		 * 
		 * @param list
		 * @return
		 */
		public boolean removeAll(List<OptionsMap> list) {
			return list.removeAll(findList(list));
		}

	}

	/**
	 * @return The {@link Map} version of this {@link OptionsMap}
	 */
	public Map<String, Object> toMap() {
		return Data.mapOf(this);
	}

	public abstract void clear();

}
